#ifndef _FragmentationFunction_H_
#define _FragmentationFunction_H_

//
// FragmentationFunction.h
//
// Created by TB on 11/17/11.
// Copyright 2011 BNL. All rights reserved.
//

#include <string>
#include <vector>

#include <TObject.h>

#include "tppmc/Enumerations.h"
#include "tppmc/Particle.h"

class TF1;

namespace tppmc {
   class Bins;
   /**
    Abstract base class for a fragmentation function.
    */
   class FragmentationFunction : public TObject {
      
   public:
      
      /** Constructor */
      FragmentationFunction()
      : mZLower(0.05)
      , mZUpper(1.)
      , mHadron(INVALID_HADRON) { }

      /** Destructor. */
      virtual ~FragmentationFunction() { }

      /**
       Fragment the input parton into hadrons at a scale Q^{2}.
       The input parton should provide at least the following (see pythpart):
       <ul>
       <li>momentum components
       <li>species
       <li>phi_spin (spin vector azimuthal angle in parton frame) if spin-
       dependent fragmentation is performed.
       </ul>
       At a minimum, the returned hadrons must have the following fields set:
       <ul>
       <li>z
       <li>species
       <li>precursor parton species
       <li>phi (via px, py) angle with respect to the parton transverse spin
       in the parton frame, in the range [-pi, pi]. This can of course be
       random, if spin-independent fragmentation is performed.
       </ul>
       The hadron should be returned in the parton frame (i.e. defined by
       z = parton 3-momentum), regardless of the input parton frame.
       */
      virtual std::vector<tppmc::Hadron> Fragment(const Parton&,
                                                  double scale) = 0;

      virtual double Evaluate(const Parton&, const Hadron&, double scale) = 0;
      
      /** Initialise the fragmentation function. */
      virtual bool Initialise();

      /**
       Returns true if the fragmentation of the input parton type
       is supported by this function.
       */
      virtual bool Supports(const Parton&) const = 0;

      /** Returns a name describing the distribution type */
      virtual std::string Name() const = 0;

      /** Returns the minimum z for which the distribution is valid */
      virtual double ZMin() const = 0;

      /** Returns the maximum z for which the distribution is valid */
      virtual double ZMax() const = 0;

      /**
       The lowest fragmentation scale allowed by the fragmentation function.
       */
      virtual double MinimumScale() const = 0;

      /**
       The highest fragmentation scale allowed by the fragmentation function.
       */
      virtual double MaximumScale() const = 0;
      
      /**
       Limit the z range of fragmentation.
       Hadrons will be generated only within this z range,
       regardless of the full range of validity of the function.
       By default the range is [ZMin(), ZMax()].
       Prints a warning if the requested range is outside the
       limits [ZMin(), ZMax()].
       */
      virtual void SetZRange(double lower, double upper);

      /**
       If set to a value other than INVALID_HADRON, and that hadron type
       is supported by this distribution, only fragment to that hadron type.
       Otherwise, fragment to a random hadron type determined by the
       distribution probability.
       See Enumerations.h for valid HadronType enumerations.
       */
      virtual void SetHadron(int i = INVALID_HADRON);

      virtual int HadronType() const;

   protected:
      
      double mZLower;
      double mZUpper;
      int mHadron;
   };

   inline bool FragmentationFunction::Initialise() {
      return true;
   }

   inline void FragmentationFunction::SetZRange(double lower, double upper) {
      mZLower = lower;
      mZUpper = upper;
   }

   inline void FragmentationFunction::SetHadron(int i) {
      mHadron = i;
   }
   
   inline int FragmentationFunction::HadronType() const {
      return mHadron;
   }

   /**
    Dummy FragmentationFunction performing no fragmentation.
   */
   class NullFragmentation : public FragmentationFunction {
   public:
      /** Constructor */
      NullFragmentation() { }
      /** Destructor. */
      virtual ~NullFragmentation() { }
      virtual std::vector<tppmc::Hadron> Fragment(const Parton&, double);
      virtual double Evaluate(const Parton&, const Hadron&, double);
      virtual bool Initialise();
      virtual bool Supports(const Parton&) const;
      virtual std::string Name() const;
      virtual double ZMin() const;
      virtual double ZMax() const;
      virtual double MinimumScale() const;
      virtual double MaximumScale() const;
   };

   inline std::vector<tppmc::Hadron> NullFragmentation::Fragment(const Parton&,
                                                                 double) {
      return std::vector<tppmc::Hadron>();
   }

   inline double NullFragmentation::Evaluate(const Parton&,
                                             const Hadron&, double) {
      return 0.;
   }

   inline bool NullFragmentation::Initialise() {
      return true;
   }

   inline bool NullFragmentation::Supports(const Parton&) const {
      return true;
   }

   inline std::string NullFragmentation::Name() const {
      return "Null";
   }

   inline double NullFragmentation::ZMin() const {
      return 0.;
   }

   inline double NullFragmentation::ZMax() const {
      return 1.;
   }

   inline double NullFragmentation::MinimumScale() const {
      return 0.;
   }

   inline double NullFragmentation::MaximumScale() const {
      return 1.e100;
   }
} // namespace tppmc

#endif
